<template>
  <div id="global-uploader">
    <!-- 上传 -->
    <uploader
      ref="uploader"
      :options="options"
      :autoStart="false"
      @file-added="onFileAdded"
      @file-success="onFileSuccess"
      @file-progress="onFileProgress"
      @file-error="onFileError"
      @file-complete="onFileComplete"
      class="uploader-app"
    >
      <uploader-unsupport></uploader-unsupport>
      <uploader-btn id="global-uploader-btn" :attrs="attrs" ref="uploadBtn"
        >选择文件</uploader-btn
      >
      <uploader-btn
        :directory="true"
        id="globalFolder-uploader-btn"
        ref="uploadFolderBtn"
        >选择文件夹</uploader-btn
      >
      <!-- <Button
        type="primary"
        v-show="foldPanelTip"
        @click="showOrHidePanelShow"
        class="foldPanelBtn"
        id="drop_myFile"
      >
        我的文件</Button
      > -->
      <!-- <uploader-drop v-show="dropShow"> -->
      <!-- fileList -->
      <uploader-list v-show="panelShow">
        <div
          class="file-panel"
          slot-scope="props"
          :class="{ collapse: collapse }"
        >
          <div class="file-title">
            <h5>文件列表</h5>
            <div class="operate">
              <img
                @click="hidePanelShow"
                src="./images/minus-round.svg"
                alt
                title="隐藏"
                class="imgStyle"
              />
            </div>
          </div>
          <ul class="file-list">
            <li v-for="file in props.fileList" :key="file.id">
              <uploader-file
                :class="'file_' + file.id"
                ref="files"
                :file="file"
                :list="true"
              ></uploader-file>
            </li>
            <div class="no-file" v-if="!props.fileList.length">
              <i class="iconfont icon-empty-file"></i> 暂无待上传文件
            </div>
          </ul>
        </div>
      </uploader-list>
      <!-- </uploader-drop> -->
    </uploader>
  </div>
</template>

<script>
/**
 *   全局上传插件
 *   调用方法：Bus.$emit('openUploader', {}) 打开文件选择框，参数为需要传递的额外参数
 *   监听函数：Bus.$on('fileAdded', fn); 文件选择后的回调
 *            Bus.$on('fileSuccess', fn); 文件上传成功的回调
 */

import { ACCEPT_CONFIG } from "./js/config";
import Bus from "./js/bus";
import SparkMD5 from "spark-md5";
const CHUNKSIZE = 50 * 1024 * 1024; //byte 20M
export default {
  data() {
    return {
      params: "",
      dropShow: false,
      foldPanelTip: false,
      isUploading: false,
      options: {
        target: this.$api.processingApi.upload,
        chunkSize: CHUNKSIZE, //"52428800" 50M,"2048000", 2M左右 单位byte 最后一个上传块的大小是可能是大于等于1倍的这个值但是小于两倍的这个值大小，默认 110241024。
        forceChunkSize: true, //强制分块，每一块都小于等于分块大小
        fileParameterName: "file", //上传文件时文件的参数名，默认 file
        maxChunkRetries: 3, //并发上传数，默认 3 最大自动失败重试上传次数，值可以是任意正整数

        testChunks: false, //是否开启服务器分片校验 是否测试每个块是否在服务端已经上传了，主要用来实现秒传、跨浏览器上传等，默认 true
        allowDuplicateUploads: true, //如果说一个文件以及上传过了是否还允许再次上传。默认的话如果已经上传了，除非你移除了否则是不会再次重新上传的，所以也就是默认值为 false。
        // 服务器分片校验函数，秒传及断点续传基础
        checkChunkUploadedByResponse: function (chunk, message) {
          let objMessage = JSON.parse(message);
          if (objMessage.skipUpload) {
            return true;
          }
          return (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0;
        },
        headers: {
          // Authorization: Ticket.get() && "Bearer " + Ticket.get().access_token
        },
        query() {
          //用于查询条件设置，接口查询条件
          //目前使用了 folderPath作用是文件上传至文件夹的路径
        },
      },
      attrs: {
        // accept: ACCEPT_CONFIG.getAll(),
      },
      panelShow: false, //选择文件后，展示上传panel
      collapse: false,
    };
  },
  mounted() {
    this.$nextTick((res) => {
      window.uploader = this.$refs.uploader.uploader; //作为系统全局变量，用于获取当前上传界面上传器uploader的对象
    });
    //文件上传
    Bus.$on("openUploader", (query) => {
      if (this.beforeFileAdd(query)) {
        return;
      }

      if (this.$refs.uploadBtn) {
        $("#global-uploader-btn").click();
      }
    });
    //文件夹上传
    Bus.$on("openFolderUploader", (query) => {
      if (this.beforeFileAdd(query)) {
        return;
      }
      if (this.$refs.uploadFolderBtn) {
        $("#globalFolder-uploader-btn").click();
      }
    });
    //文件传输列表 控制界面显示和隐藏
    Bus.$on("showUploaderFilelist", (query) => {
      this.showOrHidePanelShow();
    });
    //文件传输列表，页面切换进行隐藏
    Bus.$on("hideLoaderFilelist", (query) => {
      this.hidePanelShow();
    });
  },
  computed: {
    //Uploader实例
    uploader() {
      return this.$refs.uploader.uploader;
    },
  },
  methods: {
    onFileComplete(file) {
      if (file.isFolder) {
        file.paused = false;
      }
    },
    removeFileList() {
      // let allFileList = window.uploader.fileList;
      // for (let i = allFileList.length - 1; i >= 0; i--) {
      //   let file = allFileList[i];
      //   let isUploading = file.isUploading();
      //   if (isUploading) {
      //     alert("当前文件【" + file.name + "】正在上传！,不允许删除");
      //     break;
      //   }
      //   file.cancel();
      // }
    },
    beforeFileAdd(param) {
      let result = false;
      let allFileList = window.uploader.fileList;
      let flag = false;

      for (let i = 0; i < allFileList.length; i++) {
        let isUploading = allFileList[i].isUploading();
        let isPause = allFileList[i].paused;
        if (isUploading || isPause) {
          flag = true;
          break;
        }
        // if (!allFileList[i].completed && !allFileList[i].error) {
        //   //没用完成 以及没有错误表示正在上传
        //   flag = true;
        //   break;
        // }
      }
      if (flag) {
        result = true;
        alert("请等待当前列表文件上传完成后再继续上传！");
        return result;
      }

      this.uploader.opts.query = param || {};
      return result;
    },
    onFileAdded(file) {
      this.showPanelShow();
      this.computeMD5(file);
      Bus.$emit("fileAdded");
    },
    onFileProgress(rootFile, file, chunk) {
      // console.log(
      //   `上传中 ${file.name}，chunk：${chunk.startByte / 1024 / 1024} ~ ${
      //     chunk.endByte / 1024 / 1024
      //   }`
      // );
      console.log("chunk", chunk);
    },
    onFileSuccess(rootFile, file, response, chunk) {
      let res = JSON.parse(response);

      // 服务器自定义的错误（即虽返回200，但是是错误的情况），这种错误是Uploader无法拦截的
      if (!res.success) {
        this.$bus.$emit("fileSuccess", res);
        this.$message({ message: res.message, type: "error" });
        // 文件状态设为“失败”
        this.statusSet(file.id, "failed");
        return;
      } else {
        this.$bus.$emit("fileSuccess", res);
        Bus.$emit("fileSuccess");
      }
    },
    onFileError(rootFile, file, response, chunk) {
      this.$message({
        message: response,
        type: "error",
      });
    },
    /**
     * 计算md5，实现断点续传及秒传
     * @param file
     */
    computeMD5(file) {
      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = CHUNKSIZE; //10 * 1024 * 1000;
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();

      // 文件状态设为"计算MD5"
      this.statusSet(file.id, "md5");
      file.isMD5 = true;
      file.pause();

      loadNext();

      fileReader.onload = (e) => {
        spark.append(e.target.result);

        if (currentChunk < chunks) {
          currentChunk++;
          loadNext();

          // 实时展示MD5的计算进度
          this.$nextTick(() => {
            $(`.myStatus_${file.id}`).text(
              "校验MD5 " + ((currentChunk / chunks) * 100).toFixed(0) + "%"
            );
            //  $(`.uploader-file-resume`).attr("title","校验完成后会自动开始上传，请勿点击开始！")
            $(`.myStatus_${file.id}`).attr(
              "title",
              "校验完成后会自动开始上传，请勿点击开始！"
            );
          });
        } else {
          let md5 = spark.end();
          this.computeMD5Success(md5, file);
          console.log(
            `MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${
              file.size
            } 用时：${new Date().getTime() - time} ms`
          );
        }
      };

      fileReader.onerror = function () {
        this.error(`文件${file.name}读取出错，请检查该文件`);
        file.cancel();
      };

      function loadNext() {
        let start = currentChunk * chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;

        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },

    computeMD5Success(md5, file) {
      // 将自定义参数直接加载uploader实例的opts上
      // Object.assign(this.uploader.opts, {
      //   query: {
      //     ...this.params,
      //   },
      // });
      Object.assign(this.uploader.opts);
      file.uniqueIdentifier = md5;

      file.resume();
      file.isMD5 = false;
      this.statusRemove(file.id);
    },

    // fileListShow() {
    //   let $list = $("#global-uploader .file-list");
    //   if ($list.is(":visible")) {
    //     //隐藏
    //     this.hidePanelShow();
    //   } else {
    //     //显示
    //     this.showPanelShow();
    //   }
    // },
    close() {
      let allFileList = window.uploader.fileList;
      let flag = false;
      for (let i = 0; i < allFileList.length; i++) {
        let isUploading = allFileList[i].isUploading();
        let isPause = allFileList[i].paused;
        if (isUploading || isPause) {
          flag = true;
          break;
        }
      }

      if (!flag) {
        // this.uploader.cancel();
        this.hidePanelShow();
      } else {
        if (confirm("关闭将取消上传，是否取消？")) {
          this.uploader.cancel();
          this.hidePanelShow();
        } else {
          //对界面不做更改
        }
      }
    },

    showOrHidePanelShow() {
      let $list = $("#global-uploader .file-list");
      if ($list.is(":visible")) {
        //隐藏
        this.hidePanelShow();
      } else {
        //显示
        this.showPanelShow();
      }
    },
    hidePanelShow() {
      let $list = $("#global-uploader .file-list");
      $list.slideUp(); //fileListShow 隐藏
      this.collapse = true;
      this.foldPanelTip = true;
      this.dropShow = false;
      this.panelShow = false;
    },

    showPanelShow() {
      let $list = $("#global-uploader .file-list");
      $list.slideDown(); //fileListShow 显示
      this.collapse = false;
      this.foldPanelTip = false;
      this.dropShow = true;
      this.panelShow = true;
    },
    /**
     * 新增的自定义的状态: 'md5'、'transcoding'、'failed'
     * @param id
     * @param status
     */
    statusSet(id, status) {
      let statusMap = {
        md5: {
          text: "校验MD5",
          bgc: "#fff",
        },
        merging: {
          text: "合并中",
          bgc: "#e2eeff",
        },
        transcoding: {
          text: "转码中",
          bgc: "#e2eeff",
        },
        failed: {
          text: "上传失败",
          bgc: "#e2eeff",
        },
      };

      this.$nextTick(() => {
        $(`<p class="myStatus_${id}"></p>`)
          .appendTo(`.file_${id} .uploader-file-status`)
          .css({
            position: "absolute",
            top: "0",
            left: "0",
            right: "0",
            bottom: "0",
            zIndex: "1",
            backgroundColor: statusMap[status].bgc,
          })
          .text(statusMap[status].text);
      });
    },
    statusRemove(id) {
      this.$nextTick(() => {
        $(`.myStatus_${id}`).remove();
      });
    },

    error(msg) {
      this.$notify({
        title: "错误",
        message: msg,
        type: "error",
        duration: 2000,
      });
    },
  },
  watch: {
    isUploading: {
      handler() {},
      deep: true,
    },
  },
  destroyed() {
    Bus.$off("openUploader");
    Bus.$off("openFolderUploader");
    Bus.$off("showUploaderFilelist");
    Bus.$off("hideLoaderFilelist");
  },
  components: {},
};
</script>

<style scoped lang="scss">
#global-uploader {
  position: fixed;
  z-index: 20;
  right: 15px;
  bottom: 15px;
  .uploader-app {
    width: 520px;
  }
  .file-panel {
    background-color: #fff;
    border: 1px solid #e2e2e2;
    border-radius: 7px 7px 0 0;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);

    .file-title {
      display: flex;
      align-items: center;
      height: 40px;
      line-height: 40px;
      padding: 0 15px;
      border-bottom: 1px solid #ddd;

      .operate {
        flex: 1;
        text-align: right;
      }
    }

    .file-list {
      position: relative;
      // min-height: 240px;
      // max-height: 500px;
      height: 500px;
      overflow-x: hidden;
      overflow-y: auto;
      background-color: #fff;

      > li {
        background-color: #fff;
      }
    }

    &.collapse {
      .file-title {
        background-color: #e7ecf2;
      }
    }
  }

  .no-file {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 16px;
  }

  /deep/.uploader-file-icon {
    &:before {
      content: "" !important;
    }

    &[icon="image"] {
      background: url(./images/image-icon.png);
    }
    &[icon="video"] {
      background: url(./images/video-icon.png);
    }
    &[icon="document"] {
      background: url(./images/text-icon.png);
    }
  }

  /deep/.uploader-file-actions > span {
    margin-right: 6px;
  }
}

/* 隐藏上传按钮 */
#global-uploader-btn {
  position: absolute;
  clip: rect(0, 0, 0, 0);
}
#globalFolder-uploader-btn {
  position: absolute;
  clip: rect(0, 0, 0, 0);
}

.imgStyle {
  width: 16px;
  height: 16px;
  cursor: pointer;
  margin: 0 5px;
}
.foldPanelBtn {
  position: absolute;
  right: 0;
  bottom: 0;
}
</style>
